package com.powerbank.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.powerbank.common.entity.*;
import com.powerbank.common.exception.BusinessException;
import com.powerbank.common.result.ResultCode;
import com.powerbank.user.mapper.*;
import com.powerbank.user.service.BillingService;
import com.powerbank.user.vo.BillingInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;

/**
 * 计费服务实现类
 */
@Slf4j
@Service
public class BillingServiceImpl implements BillingService {

    @Autowired
    private RentalOrderMapper rentalOrderMapper;

    @Autowired
    private OrderBillingMapper orderBillingMapper;

    @Autowired
    private BillingRuleMapper billingRuleMapper;

    @Autowired
    private UserIdentityMapper userIdentityMapper;

    @Override
    public BillingInfoVO calculateCurrentBilling(Long orderId) {
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        // 获取计费规则
        BillingRule billingRule = getCurrentBillingRule();

        // 计算已使用时长
        LocalDateTime now = LocalDateTime.now();
        long minutes = Duration.between(order.getRentalTime(), now).toMinutes();
        int usedDuration = (int) Math.max(0, minutes);

        // 计算费用
        return calculateBillingInfo(order, billingRule, usedDuration, null);
    }

    @Override
    public Long createBillingRecord(Long orderId) {
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        // 检查是否已存在计费记录
        LambdaQueryWrapper<OrderBilling> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderBilling::getOrderId, orderId);
        if (orderBillingMapper.selectCount(wrapper) > 0) {
            log.warn("订单{}已存在计费记录", orderId);
            return null;
        }

        // 获取计费规则
        BillingRule billingRule = getCurrentBillingRule();

        // 创建计费记录
        OrderBilling billing = new OrderBilling();
        billing.setOrderId(orderId);
        billing.setOrderNo(order.getOrderNo());
        billing.setUserId(order.getUserId());
        billing.setRentalTime(order.getRentalTime());
        billing.setBillingRuleId(billingRule.getId());
        billing.setFreeDuration(getFreeDurationForUser(order.getUserId()));
        billing.setBillingStatus(0); // 计费中
        
        orderBillingMapper.insert(billing);
        return billing.getId();
    }

    @Override
    public BillingInfoVO updateBillingRecord(Long orderId) {
        RentalOrder order = rentalOrderMapper.selectById(orderId);
        if (order == null) {
            throw new BusinessException(ResultCode.ORDER_NOT_EXIST);
        }

        // 获取计费记录
        LambdaQueryWrapper<OrderBilling> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderBilling::getOrderId, orderId);
        OrderBilling billing = orderBillingMapper.selectOne(wrapper);

        if (billing == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "计费记录不存在");
        }

        // 获取计费规则
        BillingRule billingRule = billingRuleMapper.selectById(billing.getBillingRuleId());

        // 计算最终费用
        LocalDateTime returnTime = order.getReturnTime() != null ? order.getReturnTime() : LocalDateTime.now();
        long totalMinutes = Duration.between(order.getRentalTime(), returnTime).toMinutes();
        int totalDuration = (int) Math.max(0, totalMinutes);

        BillingInfoVO billingInfo = calculateBillingInfo(order, billingRule, totalDuration, returnTime);

        // 更新计费记录
        billing.setReturnTime(returnTime);
        billing.setBillingDuration(totalDuration);
        billing.setChargeableDuration(billingInfo.getChargeableDuration());
        billing.setBaseFee(billingInfo.getBaseFee());
        billing.setOvertimeFee(billingInfo.getOvertimeFee());
        billing.setTotalFee(billingInfo.getCurrentFee());
        billing.setCouponDiscount(billingInfo.getCouponDiscount());
        billing.setFinalFee(billingInfo.getActualFee());
        billing.setBillingStatus(1); // 已结算

        orderBillingMapper.updateById(billing);

        return billingInfo;
    }

    @Override
    public BillingInfoVO getBillingPreview(Long userId) {
        // 获取计费规则
        BillingRule billingRule = getCurrentBillingRule();

        BillingInfoVO billingInfo = new BillingInfoVO();
        billingInfo.setFreeDuration(getFreeDurationForUser(userId));
        billingInfo.setBaseFee(billingRule.getBaseFee());
        billingInfo.setMaxDailyFee(billingRule.getMaxDailyFee());
        billingInfo.setBillingRuleDesc(billingRule.getDescription());
        billingInfo.setFeeDetails(String.format("前%d分钟%.2f元，超时每%d分钟%.2f元，单日最高%.2f元", 
                billingRule.getBaseDuration(), billingRule.getBaseFee(),
                billingRule.getOvertimeUnit(), billingRule.getOvertimeFee(),
                billingRule.getMaxDailyFee()));

        return billingInfo;
    }

    /**
     * 计算计费信息
     */
    private BillingInfoVO calculateBillingInfo(RentalOrder order, BillingRule billingRule, 
                                               int usedDuration, LocalDateTime returnTime) {
        BillingInfoVO billingInfo = new BillingInfoVO();
        billingInfo.setOrderId(order.getId());
        billingInfo.setOrderNo(order.getOrderNo());
        billingInfo.setUsedDuration(usedDuration);

        // 获取用户免费时长
        int freeDuration = getFreeDurationForUser(order.getUserId());
        billingInfo.setFreeDuration(freeDuration);

        // 计算收费时长
        int chargeableDuration = Math.max(0, usedDuration - freeDuration);
        billingInfo.setChargeableDuration(chargeableDuration);

        // 计算基础费用
        BigDecimal baseFee = BigDecimal.ZERO;
        BigDecimal overtimeFee = BigDecimal.ZERO;

        if (chargeableDuration > 0) {
            // 基础时长内的费用
            int baseDuration = Math.min(chargeableDuration, billingRule.getBaseDuration());
            if (baseDuration > 0) {
                baseFee = billingRule.getBaseFee();
            }

            // 超时费用
            int overtimeDuration = Math.max(0, chargeableDuration - billingRule.getBaseDuration());
            if (overtimeDuration > 0) {
                int overtimeUnits = (int) Math.ceil((double) overtimeDuration / billingRule.getOvertimeUnit());
                overtimeFee = billingRule.getOvertimeFee().multiply(BigDecimal.valueOf(overtimeUnits));
            }
        }

        billingInfo.setBaseFee(baseFee);
        billingInfo.setOvertimeFee(overtimeFee);

        // 计算总费用（不超过日最高费用）
        BigDecimal totalFee = baseFee.add(overtimeFee);
        if (totalFee.compareTo(billingRule.getMaxDailyFee()) > 0) {
            totalFee = billingRule.getMaxDailyFee();
        }
        billingInfo.setCurrentFee(totalFee);
        billingInfo.setMaxDailyFee(billingRule.getMaxDailyFee());

        // 计算优惠券抵扣
        BigDecimal couponDiscount = calculateCouponDiscount(order.getCouponId(), totalFee);
        billingInfo.setCouponDiscount(couponDiscount);

        // 实际支付费用
        BigDecimal actualFee = totalFee.subtract(couponDiscount);
        if (actualFee.compareTo(BigDecimal.ZERO) < 0) {
            actualFee = BigDecimal.ZERO;
        }
        billingInfo.setActualFee(actualFee);

        // 预计下一小时费用（如果还在租借中）
        if (returnTime == null) {
            int nextHourDuration = usedDuration + 60;
            BillingInfoVO nextHourBilling = calculateBillingInfo(order, billingRule, nextHourDuration, null);
            billingInfo.setNextHourFee(nextHourBilling.getActualFee());
        }

        // 设置费用明细
        billingInfo.setFeeDetails(buildFeeDetails(billingInfo, billingRule));
        billingInfo.setBillingRuleDesc(billingRule.getDescription());

        return billingInfo;
    }

    /**
     * 获取当前有效的计费规则
     */
    private BillingRule getCurrentBillingRule() {
        LocalDateTime now = LocalDateTime.now();
        LambdaQueryWrapper<BillingRule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BillingRule::getStatus, 1)
                .le(BillingRule::getEffectiveStartTime, now)
                .and(w -> w.isNull(BillingRule::getEffectiveEndTime).or().ge(BillingRule::getEffectiveEndTime, now))
                .orderByDesc(BillingRule::getPriority)
                .last("LIMIT 1");

        BillingRule billingRule = billingRuleMapper.selectOne(wrapper);
        if (billingRule == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "未找到有效的计费规则");
        }

        return billingRule;
    }

    /**
     * 获取用户免费时长
     */
    private int getFreeDurationForUser(Long userId) {
        // 查询用户实名认证信息
        LambdaQueryWrapper<UserIdentity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserIdentity::getUserId, userId);
        UserIdentity identity = userIdentityMapper.selectOne(wrapper);

        // 新用户或特定认证等级可享受免费时长
        if (identity == null) {
            return 30; // 新用户30分钟免费
        } else if (identity.getVerifyLevel() >= 3) {
            return 60; // 高级认证60分钟免费
        } else if (identity.getVerifyLevel() >= 2) {
            return 30; // 中级认证30分钟免费
        }

        return 0; // 其他情况无免费时长
    }

    /**
     * 计算优惠券抵扣
     */
    private BigDecimal calculateCouponDiscount(Long couponId, BigDecimal totalFee) {
        if (couponId == null) {
            return BigDecimal.ZERO;
        }

        // TODO: 实现优惠券抵扣逻辑
        // 这里简化处理，实际应该查询优惠券表计算抵扣金额
        return BigDecimal.ZERO;
    }

    /**
     * 构建费用明细说明
     */
    private String buildFeeDetails(BillingInfoVO billingInfo, BillingRule billingRule) {
        StringBuilder details = new StringBuilder();
        
        if (billingInfo.getFreeDuration() > 0) {
            details.append(String.format("免费时长：%d分钟\n", billingInfo.getFreeDuration()));
        }
        
        if (billingInfo.getBaseFee().compareTo(BigDecimal.ZERO) > 0) {
            details.append(String.format("基础费用：%.2f元（前%d分钟）\n", 
                    billingInfo.getBaseFee(), billingRule.getBaseDuration()));
        }
        
        if (billingInfo.getOvertimeFee().compareTo(BigDecimal.ZERO) > 0) {
            details.append(String.format("超时费用：%.2f元\n", billingInfo.getOvertimeFee()));
        }
        
        if (billingInfo.getCouponDiscount().compareTo(BigDecimal.ZERO) > 0) {
            details.append(String.format("优惠券抵扣：-%.2f元\n", billingInfo.getCouponDiscount()));
        }
        
        details.append(String.format("实际费用：%.2f元", billingInfo.getActualFee()));
        
        return details.toString();
    }
}